﻿using System;
using System.Collections.Generic;
using System.IO;

namespace ExcelConfig
{
    using ExcelConfig.Config;
    using ExcelConfig.helpers;

    public class ExcelConfigFile : IExcelConfig
    {
        public string Name { get; internal set; } = string.Empty;
        public string SheetName { get; internal set; } = string.Empty;
        public string Key { get; internal set; } = string.Empty;
        public string FilePath { get; internal set; } = string.Empty;
        public string FullPath { get; internal set; } = string.Empty;
        public ExcelConfigFile(string filepath)
        {
            this.FilePath = helper.read_before_path(filepath);
            this.Name = helper.read_filename(filepath);
            this.FullPath = Path.Combine(FilePath, Name);
        }
        public ExcelConfigFile(string filepath = "", string name = "", string suffix = "")
        {
            if (string.IsNullOrEmpty(filepath)) this.FilePath = ExcelConfigSetting.CONFIG_PATH;
            else this.FilePath = filepath;
            if (string.IsNullOrEmpty(name)) this.Name = ExcelConfigSetting.CONFIG_DEFAULT;
            else this.Name = name;
            if (string.IsNullOrEmpty(suffix)) this.Name += ExcelConfigSetting.CONFIG_SUFFIX;
            else this.Name += suffix;
            this.FullPath = Path.Combine(FilePath, Name);
        }
        public static ExcelConfigFile Load(string path, string name)
        {
            var cfg = new ExcelConfigFile(path, name);
            return cfg;
        }
        public void SetDefault(string sheetname, string name, string value)
        {
            if (string.IsNullOrEmpty(ReadText(sheetname, name))) WriteValue(sheetname, name, value);
        }
        public void SetPath(string path)
        {
            this.FilePath = path;
            this.FullPath = Path.Combine(FilePath, Name);
        }
        public void ChangeFile(string filepath)
        {
            this.FilePath = helper.read_before_path(filepath);
            this.Name = helper.read_filename(filepath);
            this.FullPath = Path.Combine(FilePath, Name);
        }
        public void ChangeSheet(string sheetname)
        {
            if (ContainSheets(sheetname)) this.SheetName = sheetname;
        }
        public bool ContainSheets(string sheetname)
        {
            if (File.Exists(FullPath))
            foreach (var name in MiniExcel.GetSheetNames_enum(FullPath))
                if (name.Equals(sheetname)) return true;

            return false;
        }
        public IEnumerable<string> LoopSheetNames()
        {
            return MiniExcel.GetSheetNames_enum(FullPath);
        }
        public void WriteConfig(string sheetname, string key, string header, string value)
        {
            this.SheetName = sheetname;
            this.Key = key;
            if (!File.Exists(FullPath)) goto ERROR;
            if (!ContainSheets(sheetname)) goto ERROR;

            var sheet = MiniExcel.Query(FullPath, true, sheetname);
            var _sheet = new Queue<IDictionary<string, object>>();
            var edit = false;
            foreach (var _sheet_item in sheet)
            {
                if (_sheet_item[ExcelConfigSetting.COLUMN_KEY].Equals(key))
                {
                    if (_sheet_item[header] != null && _sheet_item[header].Equals(value)) return;
                    _sheet_item[header] = value;
                    _sheet.Enqueue(_sheet_item);
                    edit = true;
                    continue;
                }
                _sheet.Enqueue(_sheet_item);
            }

            if (edit)
                MiniExcel.SaveAsOverwrite(FullPath, _sheet, sheetname);
            else
            {
                var item = new Dictionary<string, object>();
                item[ExcelConfigSetting.COLUMN_KEY] = key;
                item[header] = value;
                var _s = _sheet.GetEnumerator();
                if (_s.MoveNext() && _s.Current != null)
                foreach (var field in _s.Current.Keys)
                {
                    if (item.ContainsKey(field)) continue;
                    item[field] = null;
                }
                _sheet.Enqueue(item);
                MiniExcel.SaveAsOverwrite(FullPath, _sheet, sheetname);

            }
        ERROR:
            return;
        }
        internal object readconfig_item(string sheetname, string key, string header)
        {
            this.SheetName = sheetname;
            this.Key = key;
            if (!File.Exists(FullPath)) return null;
            var sheets = MiniExcel.GetSheetNames(FullPath);
            if (!ContainSheets(sheetname)) return null;

            var sheet = MiniExcel.Query(FullPath, true, sheetname);
            foreach (var item in sheet)
            {
                if (item.ContainsKey(ExcelConfigSetting.COLUMN_KEY) && item[ExcelConfigSetting.COLUMN_KEY].Equals(key))
                    return item[header];
            }
            return null;
        }
        public string ReadConfigText(string sheetname, string key, string header)
        {
            var item = readconfig_item(sheetname, key, header);
            if (item == null) return string.Empty;
            return helper.parse_to_text(item);
        }
        public T ReadConfig<T>(string sheetname, string key, string header)
        {
            var item = readconfig_item(sheetname, key, header);
            if (item == null) goto ERROR;

            if (item is T val) return val;

            try
            {
                if (default(T) is decimal)
                {
                    var obj_text = item.ToString();
                    if (decimal.TryParse(obj_text, out var obj_val)) return (T)(object)obj_val;
                }
                return (T)item; // 高风险操作
            }
            catch { return default; }

        ERROR:
            return default;
        }
        public void WriteValue(string sheetname, string key, string value)
        {
            this.SheetName = sheetname;
            this.Key = key;
            WriteConfig(sheetname, key, ExcelConfigSetting.COLUMN_VALUE, value);
        }
        public T ReadValue<T>(string sheetname, string key)
        {
            this.SheetName = sheetname;
            this.Key = key;
            return ReadConfig<T>(sheetname, key, ExcelConfigSetting.COLUMN_VALUE);
        }
        public string ReadText(string sheetname, string key)
        {
            this.SheetName = sheetname;
            this.Key = key;
            return ReadConfigText(sheetname, key, ExcelConfigSetting.COLUMN_VALUE);
        }
        public void WriteGlobal(string key, string value)
        {
            WriteValue(ExcelConfigSetting.SHEET_Global, key, value);
        }
        public T ReadGlobal<T>(string key)
        {
            return ReadValue<T>(ExcelConfigSetting.SHEET_Global, key);
        }
        public T ReadOther<T>(string name)
        {
            return ReadConfig<T>(SheetName, Key, name);
        }
        public T ReadOtherKeyValue<T>(string name)
        {
            return ReadValue<T>(SheetName, name);
        }

        public string Text(string key)
        {
            return ReadText(this.SheetName, key);
        }
        public decimal Number(string key)
        {
            return ReadValue<decimal>(this.SheetName, key);
        }
        public IEnumerable<string> LoopArray(string key)
        {
            var arr = Text(key).Split(ExcelConfigSetting.ArraySplitChar);
            for (var i = 0; i < arr.Length; i++) if (!string.IsNullOrEmpty(arr[i])) yield return arr[i];
        }
        public string[] Array(string key)
        {
            var arr = new Queue<string>();
            foreach (var text in LoopArray(key)) arr.Enqueue(text);
            return arr.ToArray();
        }
        public bool YesOrNo(string key)
        {
            return Text(key) == ExcelConfigSetting.YES;
        }
        public void WriteText(string key, string value)
        {
            WriteValue(this.SheetName, key, value);
        }
        public void WriteYesOrNo(string key, bool value)
        {
            WriteValue(this.SheetName, key, value ? ExcelConfigSetting.YES : ExcelConfigSetting.NO);
        }
    }
}
